/*
 * Copyright (c) 2007 Compl Yue Still, All Rights Reserved.
 */

package oex;

import hbi.domain.DomainController;
import hbi.domain.Gateway;
import hbi.domain.java.JavaDomain;
import hbi.domain.java.JavaGateway;
import hbi.task.TaskScript;
import hbi.wire.java.DomainBoundary;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import oex.data.ObjectShape;
import oex.tasks.ShapeRelease;

public class ObjectWorld extends JavaDomain
{

    protected ObjectWorld() throws IOException
    {
        super(10);
    }

    @SuppressWarnings("unchecked")
    private final List<JavaGateway> explorers = new LinkedList<JavaGateway>();

    @Override
    protected <R extends DomainController> void localGatewaySetup(
        JavaGateway<? extends JavaDomain, R> gateway,
        Class<? extends R> remoteDomainType)
    {
        if (ObjectExplorer.class.isAssignableFrom(remoteDomainType))
            explorers.add(gateway);
    }

    private final ConcurrentMap<Integer, ObjectShape> shapes = //
    new ConcurrentHashMap<Integer, ObjectShape>();

    public ObjectShape newShape(String type)
    {
        ObjectShape shape = new ObjectShape(type, shapes.size());
        shapes.put(shape.oid, shape);
        return shape;
    }

    public ObjectShape getShape(int oid)
    {
        return shapes.get(oid);
    }

    public boolean removeShape(int oid)
    {
        return shapes.remove(oid) != null;
    }

    @SuppressWarnings("unchecked")
    public void publishShape2All(final ObjectShape shape)
    {
        for (Iterator<JavaGateway> gi = explorers.iterator(); gi.hasNext();)
        {
            JavaGateway<? extends ObjectWorld, ? extends ObjectExplorer> gw = gi
                .next();
            if (!gw.isAlive())
            {
                gi.remove();
                continue;
            }
            pushShape(shape, gw);
        }
    }

    @SuppressWarnings("unchecked")
    public void publishShapeRemoval2All(final int oid)
    {
        for (Iterator<JavaGateway> gi = explorers.iterator(); gi.hasNext();)
        {
            JavaGateway<? extends ObjectWorld, ? extends ObjectExplorer> gw = gi
                .next();
            if (!gw.isAlive())
            {
                gi.remove();
                continue;
            }
            gw.sendTaskAgent(ShapeRelease.class,
                new TaskScript<ShapeRelease>()
                {
                    public void run(ShapeRelease agent)
                    {
                        agent.shapeRemoved(oid);
                    }
                });
        }
    }

    public void pushAllShapes2(
        Gateway<? extends ObjectWorld, ? extends ObjectExplorer> gateway)
    {
        for (ObjectShape shape : shapes.values())
            pushShape(shape, gateway);
    }

    protected void pushShape(final ObjectShape shape,
        Gateway<? extends ObjectWorld, ? extends ObjectExplorer> gateway)
    {
        gateway.sendTaskAgent(ShapeRelease.class,
            new TaskScript<ShapeRelease>()
            {
                public void run(ShapeRelease agent)
                {
                    agent.withShape(shape.type, shape.oid);
                    for (Map.Entry<String, Object> entry : shape
                        .getPropertyEntries())
                        agent.updatedShapeProperty(entry.getKey(), entry
                            .getValue());
                }
            });
    }

    public static void main(String... args) throws Exception
    {
        int port = 11011;
        if (args.length > 0) port = Integer.parseInt(args[0]);

        System.err.println("Starting up...");
        DomainBoundary boundary = new DomainBoundary(new ObjectWorld());
        boundary.listen(new InetSocketAddress(port));
        System.err.println("Listening on port: " + port);
        System.err.println();

        File shutdownFile = File.createTempFile("ObjectWorld(", ").running", //
            new File("."));
        System.err.println("Delete file ");
        System.err.println("  \"" + shutdownFile.getCanonicalPath() + "\" ");
        System.err.println("to stop.");
        System.err.println();
        System.err.flush();
        while (shutdownFile.exists())
        {
            try
            {
                Thread.sleep(500L);
            }
            catch (InterruptedException e)
            {
                break;
            }
        }

        System.err.println("Shutting down...");
        boundary.shutdown();
    }

}
